package com.musicconvert.service;

import net.bramp.ffmpeg.FFmpeg;
import net.bramp.ffmpeg.FFmpegExecutor;
import net.bramp.ffmpeg.FFprobe;
import net.bramp.ffmpeg.builder.FFmpegBuilder;
import net.bramp.ffmpeg.probe.FFmpegProbeResult;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

@Service
public class AudioConversionService {

    @Value("${app.upload.dir:uploads}")
    private String uploadDir;

    @Value("${app.output.dir:outputs}")
    private String outputDir;

    @Value("${app.ffmpeg.path:/usr/local/bin/ffmpeg}")
    private String ffmpegPath;

    @Value("${app.ffmpeg.ffprobe.path:/usr/local/bin/ffprobe}")
    private String ffprobePath;

    private FFmpeg ffmpeg;
    private FFprobe ffprobe;

    public AudioConversionService() {
        // 延迟初始化FFmpeg，避免启动时路径问题
    }

    private void initializeFFmpeg() throws IOException {
        if (ffmpeg == null || ffprobe == null) {
            // 检查FFmpeg是否可用
            File ffmpegFile = new File(ffmpegPath);
            File ffprobeFile = new File(ffprobePath);
            
            if (!ffmpegFile.exists() || !ffmpegFile.canExecute()) {
                throw new IOException("FFmpeg not found or not executable at: " + ffmpegPath);
            }
            
            if (!ffprobeFile.exists() || !ffprobeFile.canExecute()) {
                throw new IOException("FFprobe not found or not executable at: " + ffprobePath);
            }
            
            this.ffmpeg = new FFmpeg(ffmpegPath);
            this.ffprobe = new FFprobe(ffprobePath);
        }
    }

    /**
     * 转换音频文件为MP3格式
     */
    public CompletableFuture<String> convertToMp3(MultipartFile file) {
        return convertToMp3(file, null);
    }

    /**
     * 转换音频文件为MP3格式（支持文件夹）
     */
    public CompletableFuture<String> convertToMp3(MultipartFile file, String folderName) {
        return CompletableFuture.supplyAsync(() -> {
            try {
                initializeFFmpeg();
                
                // 创建上传和输出目录
                createDirectories();
                
                // 使用绝对路径
                Path uploadPath = Paths.get(uploadDir).toAbsolutePath();
                Path outputPath = Paths.get(outputDir).toAbsolutePath();
                
                // 生成唯一文件名
                String originalFilename = file.getOriginalFilename();
                String fileExtension = getFileExtension(originalFilename);
                
                // 生成安全的输出文件名（保持原始文件名，只改变扩展名）
                String outputFilename = getSafeFilename(originalFilename);
                String inputFileName = UUID.randomUUID().toString() + "." + fileExtension;
                
                // 保存上传的文件
                Path inputPath = uploadPath.resolve(inputFileName);
                Path finalOutputPath;
                
                // 如果有文件夹名，创建对应的输出目录
                if (folderName != null && !folderName.trim().isEmpty()) {
                    Path folderPath = outputPath.resolve(folderName);
                    Files.createDirectories(folderPath);
                    finalOutputPath = getUniqueOutputPath(folderPath, outputFilename + ".mp3");
                } else {
                    finalOutputPath = getUniqueOutputPath(outputPath, outputFilename + ".mp3");
                }
                
                // 保存文件并验证
                file.transferTo(inputPath.toFile());
                
                if (!Files.exists(inputPath) || !Files.isReadable(inputPath)) {
                    throw new RuntimeException("输入文件保存失败或不可读: " + inputPath);
                }
                
                // 验证文件大小
                long fileSize = Files.size(inputPath);
                if (fileSize == 0) {
                    throw new RuntimeException("输入文件为空");
                }
                
                // 对于FLAC文件，先尝试获取音频信息来验证文件完整性
                if ("flac".equalsIgnoreCase(fileExtension)) {
                    try {
                        System.out.println("正在验证FLAC文件: " + inputPath);
                        FFmpegProbeResult probeResult = ffprobe.probe(inputPath.toString());
                        if (probeResult.getStreams().isEmpty()) {
                            throw new RuntimeException("FLAC文件不包含有效的音频流");
                        }
                        System.out.println("FLAC文件验证成功，包含 " + probeResult.getStreams().size() + " 个音频流");
                    } catch (Exception e) {
                        // 如果无法获取音频信息，尝试直接转换
                        System.err.println("警告：无法获取FLAC文件信息，尝试直接转换: " + e.getMessage());
                        System.out.println("继续尝试转换FLAC文件...");
                    }
                }
                
                // 执行转换
                FFmpegBuilder builder = new FFmpegBuilder()
                    .setInput(inputPath.toString())
                    .overrideOutputFiles(true)
                    .addOutput(finalOutputPath.toString())
                    .setFormat("mp3")
                    .setAudioCodec("libmp3lame")
                    .setAudioBitRate(192000) // 192kbps
                    .setAudioSampleRate(44100) // 44.1kHz
                    .setStrict(FFmpegBuilder.Strict.EXPERIMENTAL) // 允许实验性功能
                    .done();
                
                FFmpegExecutor executor = new FFmpegExecutor(ffmpeg, ffprobe);
                
                // 执行转换
                executor.createJob(builder).run();
                
                // 验证输出文件是否存在且有效
                if (!Files.exists(finalOutputPath)) {
                    throw new RuntimeException("转换失败：输出文件不存在");
                }
                
                long outputSize = Files.size(finalOutputPath);
                if (outputSize == 0) {
                    throw new RuntimeException("转换失败：输出文件为空");
                }
                
                // 验证输出文件是否为有效的MP3文件
                try {
                    System.out.println("正在验证输出文件: " + finalOutputPath.toAbsolutePath());
                    FFmpegProbeResult outputProbe = ffprobe.probe(finalOutputPath.toAbsolutePath().toString());
                    if (outputProbe.getStreams().isEmpty()) {
                        throw new RuntimeException("转换失败：输出文件不是有效的音频文件");
                    }
                    System.out.println("转换成功：输出文件大小 " + outputSize + " 字节，格式: " + outputProbe.getFormat().format_name);
                } catch (Exception e) {
                    System.err.println("警告：无法验证输出文件，但文件已生成: " + e.getMessage());
                    // 如果文件存在且不为空，我们假设转换成功
                    if (outputSize > 0) {
                        System.out.println("转换可能成功：输出文件大小 " + outputSize + " 字节");
                    } else {
                        throw new RuntimeException("转换失败：输出文件无效");
                    }
                }
                
                // 清理临时文件
                try {
                    Files.deleteIfExists(inputPath);
                } catch (IOException e) {
                    // 忽略删除失败
                    System.err.println("警告：无法删除临时文件: " + e.getMessage());
                }
                
                return finalOutputPath.toString();
                
            } catch (Exception e) {
                // 清理可能存在的临时文件
                try {
                    String uniqueId = UUID.randomUUID().toString();
                    Path inputPath = Paths.get(uploadDir).toAbsolutePath().resolve(uniqueId + "." + getFileExtension(file.getOriginalFilename()));
                    Files.deleteIfExists(inputPath);
                } catch (IOException cleanupEx) {
                    // 忽略清理错误
                }
                
                throw new RuntimeException("音频转换失败: " + e.getMessage(), e);
            }
        });
    }

    /**
     * 创建ZIP文件
     */
    public String createZipFile(Map<String, Object> request) throws IOException {
        @SuppressWarnings("unchecked")
        List<Map<String, Object>> files = (List<Map<String, Object>>) request.get("files");
        
        if (files == null || files.isEmpty()) {
            throw new IOException("没有文件需要打包");
        }
        
        // 创建临时ZIP文件
        String zipFileName = "converted_audio_" + System.currentTimeMillis() + ".zip";
        String zipPath = outputDir + "/" + zipFileName;
        
        try (ZipOutputStream zipOut = new ZipOutputStream(new FileOutputStream(zipPath))) {
            for (Map<String, Object> fileInfo : files) {
                String convertedFile = (String) fileInfo.get("convertedFile");
                String originalFile = (String) fileInfo.get("originalFile");
                String folderName = (String) fileInfo.get("folderName");
                
                if (convertedFile != null) {
                    File file = new File(convertedFile);
                    if (file.exists()) {
                        // 确定ZIP中的路径
                        String zipEntryPath;
                        if (folderName != null && !folderName.trim().isEmpty()) {
                            zipEntryPath = folderName + "/" + file.getName();
                        } else {
                            zipEntryPath = file.getName();
                        }
                        
                        ZipEntry zipEntry = new ZipEntry(zipEntryPath);
                        zipOut.putNextEntry(zipEntry);
                        
                        // 复制文件内容到ZIP
                        try (FileInputStream fis = new FileInputStream(file)) {
                            byte[] buffer = new byte[1024];
                            int length;
                            while ((length = fis.read(buffer)) > 0) {
                                zipOut.write(buffer, 0, length);
                            }
                        }
                        
                        zipOut.closeEntry();
                    }
                }
            }
        }
        
        return zipPath;
    }

    /**
     * 获取文件信息
     */
    public AudioInfo getAudioInfo(MultipartFile file) throws IOException {
        try {
            initializeFFmpeg();
            
            // 确保上传目录存在
            createDirectories();
            
            // 使用绝对路径
            Path uploadPath = Paths.get(uploadDir).toAbsolutePath();
            
            // 生成安全的临时文件名
            String originalFilename = file.getOriginalFilename();
            String safeFilename = originalFilename != null ? 
                originalFilename.replaceAll("[^a-zA-Z0-9.-]", "_") : "unknown";
            String tempFileName = "temp_" + System.currentTimeMillis() + "_" + safeFilename;
            
            // 确保文件名不会太长
            if (tempFileName.length() > 100) {
                String extension = getFileExtension(safeFilename);
                tempFileName = "temp_" + System.currentTimeMillis() + "." + extension;
            }
            
            Path tempPath = uploadPath.resolve(tempFileName);
            
            // 保存文件
            file.transferTo(tempPath.toFile());
            
            try {
                // 验证文件是否存在且可读
                if (!Files.exists(tempPath)) {
                    throw new IOException("临时文件创建失败: " + tempPath);
                }
                
                if (!Files.isReadable(tempPath)) {
                    throw new IOException("临时文件不可读: " + tempPath);
                }
                
                // 验证文件大小
                long fileSize = Files.size(tempPath);
                if (fileSize == 0) {
                    throw new IOException("文件为空");
                }
                
                // 检查文件扩展名
                String fileExtension = getFileExtension(originalFilename);
                if ("flac".equalsIgnoreCase(fileExtension)) {
                    // 对于FLAC文件，先检查文件头
                    try (FileInputStream fis = new FileInputStream(tempPath.toFile())) {
                        byte[] header = new byte[4];
                        if (fis.read(header) == 4) {
                            // FLAC文件应该以 "fLaC" 开头
                            if (header[0] != 'f' || header[1] != 'L' || header[2] != 'a' || header[3] != 'C') {
                                System.err.println("警告：FLAC文件头不正确，可能已损坏");
                            }
                        }
                    } catch (IOException e) {
                        System.err.println("警告：无法读取FLAC文件头: " + e.getMessage());
                    }
                }
                
                // 使用绝对路径进行FFprobe调用
                String absolutePath = tempPath.toAbsolutePath().toString();
                System.out.println("正在分析文件: " + absolutePath);
                
                FFmpegProbeResult probeResult = ffprobe.probe(absolutePath);
                
                // 检查是否有音频流
                if (probeResult.getStreams().isEmpty()) {
                    throw new IOException("文件不包含有效的音频流");
                }
                
                net.bramp.ffmpeg.probe.FFmpegFormat format = probeResult.getFormat();
                net.bramp.ffmpeg.probe.FFmpegStream stream = probeResult.getStreams().get(0);
                
                AudioInfo info = new AudioInfo();
                info.setDuration(format.duration);
                info.setSize(format.size);
                info.setBitRate(format.bit_rate);
                info.setSampleRate(stream.sample_rate);
                info.setChannels(stream.channels);
                info.setCodec(stream.codec_name);
                
                return info;
            } finally {
                // 清理临时文件
                try {
                    Files.deleteIfExists(tempPath);
                } catch (IOException e) {
                    // 忽略删除失败
                    System.err.println("警告：无法删除临时文件: " + e.getMessage());
                }
            }
        } catch (Exception e) {
            // 提供更详细的错误信息
            String errorMessage = "获取音频信息失败";
            if (e.getMessage().contains("Invalid data found when processing input")) {
                errorMessage += ": 文件可能已损坏或格式不支持";
            } else if (e.getMessage().contains("No such file or directory")) {
                errorMessage += ": 文件路径错误";
            } else if (e.getMessage().contains("Permission denied")) {
                errorMessage += ": 文件访问权限不足";
            } else {
                errorMessage += ": " + e.getMessage();
            }
            throw new IOException(errorMessage, e);
        }
    }

    /**
     * 检查文件格式是否支持
     */
    public boolean isSupportedFormat(String filename) {
        if (filename == null) return false;
        String extension = getFileExtension(filename).toLowerCase();
        return extension.matches("(m4a|wav|flac|aac|ogg|wma)");
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String filename) {
        if (filename == null || filename.lastIndexOf(".") == -1) {
            return "";
        }
        return filename.substring(filename.lastIndexOf(".") + 1);
    }

    /**
     * 生成安全的文件名（保持原始文件名，移除扩展名）
     */
    private String getSafeFilename(String originalFilename) {
        if (originalFilename == null) {
            return "unknown";
        }
        
        // 移除扩展名
        String nameWithoutExtension = originalFilename;
        int lastDotIndex = originalFilename.lastIndexOf(".");
        if (lastDotIndex > 0) {
            nameWithoutExtension = originalFilename.substring(0, lastDotIndex);
        }
        
        // 替换不安全的字符
        String safeName = nameWithoutExtension
            .replaceAll("[<>:\"/\\\\|?*]", "_")  // 替换Windows不允许的字符
            .replaceAll("\\s+", "_")             // 替换空格为下划线
            .replaceAll("_+", "_")               // 合并多个下划线
            .trim();
        
        // 确保文件名不为空
        if (safeName.isEmpty()) {
            safeName = "unknown";
        }
        
        // 限制文件名长度
        if (safeName.length() > 100) {
            safeName = safeName.substring(0, 100);
        }
        
        return safeName;
    }

    /**
     * 获取唯一的输出文件路径（处理文件名冲突）
     */
    private Path getUniqueOutputPath(Path directory, String filename) {
        Path filePath = directory.resolve(filename);
        
        // 如果文件不存在，直接返回
        if (!Files.exists(filePath)) {
            return filePath;
        }
        
        // 如果文件存在，添加计数器后缀
        String nameWithoutExtension = filename;
        String extension = "";
        int lastDotIndex = filename.lastIndexOf(".");
        if (lastDotIndex > 0) {
            nameWithoutExtension = filename.substring(0, lastDotIndex);
            extension = filename.substring(lastDotIndex);
        }
        
        int counter = 1;
        Path uniquePath;
        do {
            uniquePath = directory.resolve(nameWithoutExtension + "_" + counter + extension);
            counter++;
        } while (Files.exists(uniquePath));
        
        return uniquePath;
    }

    /**
     * 创建必要的目录
     */
    private void createDirectories() throws IOException {
        Files.createDirectories(Paths.get(uploadDir));
        Files.createDirectories(Paths.get(outputDir));
    }

    /**
     * 检查FFmpeg是否可用
     */
    public boolean isFFmpegAvailable() {
        try {
            initializeFFmpeg();
            return true;
        } catch (Exception e) {
            System.err.println("FFmpeg不可用: " + e.getMessage());
            return false;
        }
    }

    /**
     * 获取FFmpeg版本信息
     */
    public String getFFmpegVersion() {
        try {
            initializeFFmpeg();
            // 这里可以添加获取FFmpeg版本的逻辑
            return "FFmpeg已安装";
        } catch (Exception e) {
            return "FFmpeg未安装或配置错误: " + e.getMessage();
        }
    }

    /**
     * 音频信息类
     */
    public static class AudioInfo {
        private double duration;
        private long size;
        private long bitRate;
        private int sampleRate;
        private int channels;
        private String codec;

        // Getters and Setters
        public double getDuration() { return duration; }
        public void setDuration(double duration) { this.duration = duration; }
        
        public long getSize() { return size; }
        public void setSize(long size) { this.size = size; }
        
        public long getBitRate() { return bitRate; }
        public void setBitRate(long bitRate) { this.bitRate = bitRate; }
        
        public int getSampleRate() { return sampleRate; }
        public void setSampleRate(int sampleRate) { this.sampleRate = sampleRate; }
        
        public int getChannels() { return channels; }
        public void setChannels(int channels) { this.channels = channels; }
        
        public String getCodec() { return codec; }
        public void setCodec(String codec) { this.codec = codec; }
    }
} 